#include "preprocessor.h"

#include <list>
#include <string>
#include <vector>

#include "arithmetic.h"
#include "location.h"
#include "token.h"

using namespace Token;
using TokenList = std::list<Token::BaseToken*>;

std::vector<int> StringConverter(const std::string& str) {
    std::vector<int> ret;
    char buf[4];
    *(int*)buf = 0;
    for (int i = 0, pos = 0; i < str.size(); ++i) {
        buf[pos++] = str[i];
        if (pos == 4) {
            ret.push_back(*(int*)buf), pos = 0;
            *(int*)buf = 0;
        }
    }
    ret.push_back(*(int*)buf);
    // for (auto x : ret) printf("%08x\n", x);
    return ret;
}

TokenList Preprocessor::Scan(const TokenList& tokens) {
    TokenList defs, ret;
    int strID = 0;
    for (auto it = tokens.begin(); it != tokens.end(); ++it) {
        auto tok = *it;
        auto loc = tok->loc;
        if (auto str = tok->As<String>()) {
            // string to int array
            auto arr = StringConverter(str->str);
            std::string name = "$" + std::to_string(strID++);
            defs.push_back(new TypeDecl(loc, Tag::ConstQualifier));
            defs.push_back(new TypeDecl(loc, Tag::IntType));
            defs.push_back(new Identifier(loc, name));
            defs.push_back(new Operator(loc, Tag::LBracket));
            defs.push_back(new Literal(loc, ArithValue((int)arr.size())));
            defs.push_back(new Operator(loc, Tag::RBracket));
            defs.push_back(new Operator(loc, Tag::Assign));
            defs.push_back(new Operator(loc, Tag::LBrace));
            for (int i = 0; i < arr.size(); ++i) {
                if (i) defs.push_back(new Operator(loc, Tag::Comma));
                defs.push_back(new Literal(loc, ArithValue(arr[i])));
            }
            defs.push_back(new Operator(loc, Tag::RBrace));
            defs.push_back(new Operator(loc, Tag::Semi));
            ret.push_back(new Identifier(loc, name));
            continue;
        }
        if (auto id = tok->As<Identifier>()) {
            // expand macros
            if (id->name == "starttime" || id->name == "stoptime") {
                ++it;
                ret.push_back(new Identifier(loc, "_sysy_" + id->name));
                ret.push_back(new Operator(loc, Tag::LParen));
                ret.push_back(new Literal(loc, ArithValue(loc.line)));
                continue;
            }
        }
        ret.push_back(tok);
    }
    defs.insert(defs.end(), ret.begin(), ret.end());
    return defs;
}

void* Preprocessor::Run(void* input) {
    auto tokens = *(TokenList*)input;
    auto ret = new TokenList(Scan(tokens));
    return ret;
}
